/*
 * Copyright (c) 2008, Artur Emagulov
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * 23.08.2008
 * boot/arm/head.S - kernel bootstrap code
 * It is assumed that this code is running from DRAM, so that
 * it was placed into DRAM by early boot code from ROM or any other
 * source.
 */

#define ENTRY(x) .global x; .align; x##:

#define HAVE_ASM_USCORE 1
#define ASM     1

#include <arm/platform.h>
#include <arm/mach/hardware.h>

	.extern __arch_setup_hw_profile
	.extern load_kernel_img
	.extern tail

	.text
	.globl _start
	.type _start, %function

_start:
	/*
	 * Exception vector
	 *
	 * This table will be copied to an appropriate location.
	 * (the location is platform specific.)
	 */
	ldr	pc, reset_target		/* 0x00 mode: svc */
	ldr	pc, undefined_target	/* 0x04 mode: ? */
	ldr	pc, swi_target			/* 0x08 mode: svc */
	ldr	pc, prefetch_target		/* 0x0c mode: abort */
	ldr	pc, abort_target		/* 0x10 mode: abort */
	nop							/* 0x14 reserved */
	ldr	pc, irq_target			/* 0x18 mode: irq */
	ldr	pc, fiq_target			/* 0x1c mode: fiq */

reset_target:		.word	__boot_up
undefined_target:	.word	__undefined_entry
swi_target:			.word	__exception_entry
prefetch_target:	.word	__prefetch_entry
abort_target:		.word	__abort_entry
irq_target:			.word	__exception_entry
fiq_target:			.word	__exception_entry
_vector_end:		.word	_vector_end

/*
 * TODO organize this entry as a reset handler
 */
    .type __exception_entry, %function
__exception_entry:
    b __exception_entry
    .align

    .type __prefetch_entry, %function
__prefetch_entry:
    b __prefetch_entry
    .align

    .type __undefined_entry, %function
__undefined_entry:
    b __undefined_entry
    .align

    .type __abort_entry, %function
__abort_entry:
    b __abort_entry
    .align

/*
 * TODO organize this entry as a reset handler
 */
	.type __boot_up, %function
__boot_up:
	msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | PSR_SVC_MODE @ ensure svc mode
	bl __create_page_tables		/* prepare memory regions */
    ldr	r13,=__start_kernel		/* call 'C' kernel code */
    adr	lr,	__enable_mmu		/* return (PIC) address */
	adr	r0, __arm926_setup		/* b __arm926_setup */
    mov pc, r0					/* __start_kernel */
    .align

	.type __start_kernel, %function
__start_kernel:
    ldr r0, =__end_of_head		/* setup parameters */
#if 0
    ldr r3, =PAGE_OFFSET		/* virtual address base */
    ldr r5, =PHYS_OFFSET		/* RAM base	*/
    sub r0, r0, r5
    add r0, r0, r3				/* virtual address of kernel image */
#endif
    ldr	sp, =__boot_stack_top	/* Set stack from boot.lds */
#if 0
    sub sp, sp, r5
    add sp, sp, r3				/* virtual address of boot stack */
#endif

    bl load_kernel_img			/* load and setup ELF image */
#if 1
    mov r5,r0
    ldr r0,=0xC0002000      /* HW_PROFILE_INFO */
    bl __arch_setup_hw_profile
#endif

    mov r0,#1
    ldr r1,=0xC0002000      /* HW_PROFILE_INFO */
//    push {r0}
//    pop {pc}
    mov pc, r5;					/* branch to kmain */
	.align

	/* STEP 5: Initialize the MMU, caches, and write buffer. */
    .type   __enable_mmu, %function
__enable_mmu:
    orr     r0, r0, #(CR_A | CR_M)
	mov		r5, #-1							@ for domain disable...
	mcr     p15, 0, r5, c3, c0, 0           @ load domain access register
    mcr     p15, 0, r4, c2, c0, 0           @ load page table pointer
	mov     r0, r0
    mcr     p15, 0, r0, c1, c0, 0           @ write control reg
    mrc     p15, 0, r3, c0, c0, 0           @ read id reg
    mov     r3, r3
    mov     r3, r3
    mov     pc, r13							/* goto __start_kernel		*/
	.align

	/*
	 * TODO Processor and machine dependent init must be called \
	 * indirectly
	 */
    .type   __arm926_setup, %function
__arm926_setup:
    mov     r0, #0
    mcr     p15, 0, r0, c7, c7              @ invalidate I,D caches on v4
    mcr     p15, 0, r0, c7, c10, 4          @ drain write buffer on v4
    mcr     p15, 0, r0, c8, c7              @ invalidate I,D TLBs on v4
    mrc     p15, 0, r0, c1, c0              @ get control register v4
    ldr     r5, arm926_cr1_clear
    bic     r0, r0, r5
    ldr     r5, arm926_cr1_set
    orr     r0, r0, r5
    mov     pc, lr							/* return to __enable_mmu */
    .size   __arm926_setup, . - __arm926_setup

/*
 * Setup the initial page tables.  We only setup the barest
 * amount which are required to get the kernel running, which
 * generally means mapping in the kernel code.
 *
 * Returns:
 *  r0, r3, r6, r7 corrupted
 *  r4 = physical page table address
 */
/* #define PAGE_OFFSET 0xC0000000 */
/* #define PHYS_OFFSET 0x80000000 */
/* #define KERNEL_PGD 0xC0004000 */
/* #define KERNEL_IVEC_PTBL 0xC0001000 */
	.type	__create_page_tables, %function
__create_page_tables:
	ldr r3, =PAGE_OFFSET		/* virtual address base */
	ldr r5, =PHYS_OFFSET		/* RAM base	*/
	ldr	r4, =KERNEL_PGD			/* r4 - virtual address of page table	*/
	sub r4, r4, r3
	add r4, r4, r5				/* physical address of page table */

	/*
	 * Clear the 16K level 1 swapper page table
	 */
	mov	r0, r4
	mov	r3, #0
	add	r6, r0, #0x4000
1:	str	r3, [r0], #4
	str	r3, [r0], #4
	str	r3, [r0], #4
	str	r3, [r0], #4
	teq	r0, r6
	bne	1b

	/*
	 *  Master L1 PTE flags:
	 *  r7=00000000000000000000010000011110
	 */
	ldr r7, =0x0000041E

	/*
	 * Map first 1MB of loader base memory into Master L1.
	 * Kernel itself must be loaded (relocated) so that
	 * next code will map real kernel.
	 */
	mov	r6, pc, lsr #20			/* start of kernel section */
	orr	r3, r7, r6, lsl #20		/* flags + kernel base */
	str	r3, [r4, r6, lsl #2]	/* PTE id = r6*4 */

	/*
	 * TODO CRITICAL!!! Must check for possible overflow in L1 page table
	 * Map I/O address space
	 */
	ldr r7,=0x00000812			/* non-cachable / non-buffered */
	orr	r3, r7, #(P_IO_BASE & 0xfff00000)		/* descriptor content (physical base + AP flags) to be stored in L1 */
	add r6, r4, #((V_IO_BASE + V_IO_SIZE) & 0xfff00000) >> 18	/* final index of IO region */
	add r0, r4, #(V_IO_BASE & 0xfff00000) >> 18	/* Base index of IO region in L1 */
2:
	str r3, [r0], #4			/* store descriptor in the next index */
	add r3, r3, #1 << 20		/* increment physical address by 1MB */
	teq	r0, r6					/* if (r0 != r6) then */
	bne	2b						/* continue  */

	/*
	 * TODO CRITICAL!!! Must check for possible overflow in L1 page table
	 * Map I/O address space
	 */
	ldr r7,=0x00000812			/* non-cachable / non-buffered */
	orr	r3, r7, #(CP_IO_BASE & 0xfff00000)		/* descriptor content (physical base + AP flags) to be stored in L1 */
	add r6, r4, #((C_IO_BASE + C_IO_SIZE) & 0xfff00000) >> 18	/* final index of IO region */
	add r0, r4, #(C_IO_BASE & 0xfff00000) >> 18	/* Base index of IO region in L1 */
3:
	str r3, [r0], #4			/* store descriptor in the next index */
	add r3, r3, #1 << 20		/* increment physical address by 1MB */
	teq	r0, r6					/* if (r0 != r6) then */
	bne	3b						/* continue  */

	/*
	 * Now setup the pagetable for interrupt vector.
	 * Here we map section entry (i.e. 1MB of memory),
	 * It is not good idea to use 1MB section for
	 * just 32 Bytes of exception vector that is why we must
	 * write a:
	 * TODO IMPORTANT!!! Use coarse or fine page entries instead of 1MB section for ivector mapping.
	 */
	ldr r7, =0x00000011						/* Coarse page table flags */
	ldr r6, =PAGE_OFFSET					/* virtual address base */
	ldr r5, =PHYS_OFFSET					/* RAM base	*/
	ldr r3, =KERNEL_IVEC_PTBL				/* Base address of page table, 1-st L2 page table just after L1 table */
	sub r3, r3, r6
	add r3, r3, r5							/* physical address of page table */

	orr	r5, r3, r7							/* descriptor content (physical base + AP flags) to be stored in L1 */
	ldr r0, =(IRQ_VECTOR_BASE & 0xfff00000)
	str r5, [r4, r0, lsr #18]				/* store page table address in L1 PT entry */

	ldr r7, =0x0000001E						/* small page table entry with SVC r/w permissions */
	ldr r5, =(PHYS_OFFSET & 0xfffff000) 	/* RAM base	*/
	orr r5, r5, r7
	mov r0, #(IRQ_VECTOR_BASE & 0x000ff000)
	str r5, [r3, r0, lsr #10]

	/*
	 * Then map RAM_SIZE of ram, starting at PAGE_OFFSET
	 * The kernel itself is located in this area
	 * that is why, after this mapping, we can branch
	 * to position dependent code of kernel.
	 */
	ldr r7, =0x0000041E
	orr	r3, r7, #(PHYS_OFFSET & 0xfff00000)		/* descriptor content (physical base + AP flags) to be stored in L1 */
	ldr r0, =PAGE_OFFSET
	ldr r6, =0xfff00000
	and r0, r0, r6
	add	r0, r4, r0, lsr #18						/* base index */
	add r6, r0, #PHYS_RAM_SIZE >> 18			/* final index */
4:
	str r3, [r0], #4			/* store descriptor in the next index */
	add r3, r3, #1 << 20		/* increment physical address by 1MB */
	teq	r0, r6					/* if (r0 != r6) then */
	bne	4b						/* continue  */

	mov	pc, lr
	.size   __create_page_tables, . -__create_page_tables

    /*
     *  R
     * .RVI ZFRS BLDP WCAM
     * .011 0001 ..11 0101
     */
    .type   arm926_cr1_clear, %object
    .type   arm926_cr1_set, %object
arm926_cr1_clear:
    .word   0x7f3f
arm926_cr1_set:
    .word   0x1135

    .align
kern_img:
	.end		/* The end of code */

